﻿namespace EasyWeb
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;

    // The class helps read/write the properties of field definition into database.
    public sealed class EWSerializationInfo : IDictionary<string, string>
    {
        private readonly IDictionary<string, string> states = new Dictionary<string, string>(20);

        public void AddValue(string name, Guid value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString("N", CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, bool value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, byte value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, char value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, DateTime value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.Ticks.ToString(CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, DateTimeOffset value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = string.Format("{0},{1}", value.Ticks.ToString(CultureInfo.InvariantCulture), value.Offset.Ticks.ToString(CultureInfo.InvariantCulture));
        }

        public void AddValue(string name, TimeSpan value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.Ticks.ToString(CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, decimal value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, double value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, float value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, short value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, int value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, long value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        [CLSCompliant(false)]
        public void AddValue(string name, sbyte value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        [CLSCompliant(false)]
        public void AddValue(string name, ushort value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        [CLSCompliant(false)]
        public void AddValue(string name, uint value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        [CLSCompliant(false)]
        public void AddValue(string name, ulong value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.ToString(CultureInfo.InvariantCulture);
        }

        public void AddValue(string name, Guid? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString("N", CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, bool? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, byte? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, char? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, DateTime? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.Ticks.ToString(CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, DateTimeOffset? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ?
                string.Format("{0},{1}", value.Value.Ticks.ToString(CultureInfo.InvariantCulture), value.Value.Offset.Ticks.ToString(CultureInfo.InvariantCulture))
                : null;
        }

        public void AddValue(string name, TimeSpan? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.Ticks.ToString(CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, decimal? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, double? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, float? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, short? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, int? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, long? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        [CLSCompliant(false)]
        public void AddValue(string name, sbyte? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        [CLSCompliant(false)]
        public void AddValue(string name, ushort? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        [CLSCompliant(false)]
        public void AddValue(string name, uint? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        [CLSCompliant(false)]
        public void AddValue(string name, ulong? value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
        }

        public void AddValue(string name, string value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            this.states[name] = value;
        }

        public Guid GetGuid(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return Guid.ParseExact(GetValueCore(name, true), "N");
        }

        public bool GetBoolean(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return bool.Parse(GetValueCore(name, true));
        }

        public byte GetByte(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return byte.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture);
        }

        public char GetChar(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return char.Parse(GetValueCore(name, true));
        }

        public DateTime GetDateTime(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return new DateTime(long.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture));
        }

        public DateTimeOffset GetDateTimeOffset(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var all = GetValueCore(name, true).Split(',');
            var ticks = long.Parse(all[0], CultureInfo.InvariantCulture);
            var offset = new TimeSpan(long.Parse(all[1], CultureInfo.InvariantCulture));
            return new DateTimeOffset(ticks, offset);
        }

        public TimeSpan GetTimeSpan(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return new TimeSpan(long.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture));
        }

        public decimal GetDecimal(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return decimal.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture);
        }

        public double GetDouble(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return double.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture);
        }

        public float GetSingle(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return float.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture);
        }

        public short GetInt16(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return short.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture);
        }

        public int GetInt32(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return int.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture);
        }

        public long GetInt64(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return long.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture);
        }

        [CLSCompliant(false)]
        public sbyte GetSByte(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return sbyte.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture);
        }

        [CLSCompliant(false)]
        public ushort GetUInt16(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return ushort.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture);
        }

        [CLSCompliant(false)]
        public uint GetUInt32(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return uint.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture);
        }

        [CLSCompliant(false)]
        public ulong GetUInt64(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return ulong.Parse(GetValueCore(name, true), CultureInfo.InvariantCulture);
        }

        public Guid? GetNullableGuid(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (Guid?)GetGuid(name);
        }

        public bool? GetNullableBoolean(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (bool?)GetBoolean(name);
        }

        public byte? GetNullableByte(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (byte?)GetByte(name);
        }

        public char? GetNullableChar(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (char?)GetChar(name);
        }

        public DateTime? GetNullableDateTime(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (DateTime?)GetDateTime(name);
        }

        public DateTimeOffset? GetNullableDateTimeOffset(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (DateTimeOffset?)GetDateTimeOffset(name);
        }

        public TimeSpan? GetNullableTimeSpan(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (TimeSpan?)GetTimeSpan(name);
        }

        public decimal? GetNullableDecimal(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (decimal?)GetDecimal(name);
        }

        public double? GetNullableDouble(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (double?)GetDouble(name);
        }

        public float? GetNullableSingle(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (float?)GetSingle(name);
        }

        public short? GetNullableInt16(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (short?)GetInt16(name);
        }

        public int? GetNullableInt32(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (int?)GetInt32(name);
        }

        public long? GetNullableInt64(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (long?)GetInt64(name);
        }

        [CLSCompliant(false)]
        public sbyte? GetNullableSByte(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (sbyte?)GetSByte(name);
        }

        [CLSCompliant(false)]
        public ushort? GetNullableUInt16(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (ushort?)GetUInt16(name);
        }

        [CLSCompliant(false)]
        public uint? GetNullableUInt32(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (uint?)GetUInt32(name);
        }

        [CLSCompliant(false)]
        public ulong? GetNullableUInt64(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var value = GetValueCore(name, false);
            return string.IsNullOrEmpty(value) ? null : (ulong?)GetUInt64(name);
        }

        public string GetString(string name)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            return this.states[name];
        }

        public bool ContainsKey(string key)
        {
            return this.states.ContainsKey(key);
        }

        public ICollection<string> Keys
        {
            get { return this.states.Keys; }
        }

        public bool Remove(string key)
        {
            return this.states.Remove(key);
        }

        public bool TryGetValue(string key, out string value)
        {
            return this.states.TryGetValue(key, out value);
        }

        public ICollection<string> Values
        {
            get { return this.states.Values; }
        }

        public void Clear()
        {
            this.states.Clear();
        }

        public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
        {
            this.states.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return this.states.Count; }
        }

        public bool IsReadOnly
        {
            get { return this.states.IsReadOnly; }
        }

        void IDictionary<string, string>.Add(string key, string value)
        {
            this.states.Add(key, value);
        }

        string IDictionary<string, string>.this[string key]
        {
            get { return this.states[key]; }
            set { this.states[key] = value; }
        }

        void ICollection<KeyValuePair<string, string>>.Add(KeyValuePair<string, string> item)
        {
            this.states.Add(item);
        }

        bool ICollection<KeyValuePair<string, string>>.Contains(KeyValuePair<string, string> item)
        {
            return this.states.Contains(item);
        }

        bool ICollection<KeyValuePair<string, string>>.Remove(KeyValuePair<string, string> item)
        {
            return this.states.Remove(item);
        }

        IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
        {
            return this.states.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.states.GetEnumerator();
        }

        internal TEnum GetEnum<TEnum>(string name)
            where TEnum : struct
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var text = GetValueCore(name, true);
            return (TEnum)Enum.Parse(typeof(TEnum), text, false);
        }

        internal void AddValue<TEnum>(string name, TEnum value)
            where TEnum : struct
        {
            EWGuard.IsNotNullOrEmpty(name, "name");
            var text = Enum.GetName(typeof(TEnum), value);
            this.states[name] = text;
        }

        private string GetValueCore(string name, bool throwError)
        {
            var value = default(string);

            if (!this.states.TryGetValue(name, out value) && throwError)
            {
                throw new InvalidOperationException(string.Format("The specified name '{0}' does not exist.", name));
            }

            return value;
        }
    }
}
